/*******************************************************************************
 * @brief  flash opt
 *******************************************************************************/
#include "stdint.h"
#include "sys_task.h"
#include "bsp_api.h"
#include "nor_cmd.h"
#include "str_ext.h"
#include "framework.h"

/*******************************************************************************
 * @brief  init
 *******************************************************************************/
obj_init_app(flash_opt_init);
void nor_flash_task(const void *argument);
osThreadDef(nor_flash_task, osPriorityNormal, 1, 0x400);

/*******************************************************************************
 * @brief  debug
 *******************************************************************************/
#define FLASH_DBG_EN 1

#if FLASH_DBG_EN > 0
#define flash_dbg_print dbg_print
#define flash_dbg_puts dbg_puts
#else
#define flash_dbg_print(...)
#define flash_dbg_puts(...)
#endif

void nor_flash_opt(void);

/*******************************************************************************
 * @brief  gpio cfg list
 *******************************************************************************/
struct
{
	volatile uint16_t check_req; // 0空闲 1-运行，地址范围 start~end
	volatile uint16_t read_req;	 // 0空闲 1-运行，地址范围 start~end
	volatile uint16_t sig_read_req;
	volatile uint16_t sig_write_req;
	uint16_t err;
	volatile uint16_t inited;
	uint32_t id;
	uint32_t dat;
	uint32_t dat_first; // 数据递增起始值
	uint32_t addr;
	uint32_t addr_start;	  // 扇区对齐
	volatile uint32_t amount; // 扇区对齐
	uint32_t tm_used;
	dword_tt *buff;
	uint32_t chk_err_cnt;
	uint32_t chk_cnt;
} nortst;

/*******************************************************************************
 * @brief  flash_opt_init
 * \param[in] none
 * \retval: none
 *******************************************************************************/
void flash_opt_init(void)
{
	os_obj.tid_flash_task = osThreadCreate(osThread(nor_flash_task), NULL);
	dbg_trace_zero(os_obj.tid_flash_task, "flash task creat fail");
}

/*******************************************************************************
 * @brief  flash task
 * \param[in] argument
 * \retval: none
 *******************************************************************************/
void nor_flash_task(const void *argument)
{
	osDelay(10);

	nortst.inited = 1;
	nortst.addr_start = 0;
	nortst.amount = 2 * 1024 * 1024;
	nortst.dat_first = 0x12345678;

	nor_id_read((uu8*)&nortst.id);
	
	while (1)
	{
		osDelay(10);

		if (0 == (nortst.check_req +
				  nortst.read_req +
				  nortst.sig_read_req +
				  nortst.sig_write_req))
			continue;

		nortst.buff = malloc(4096 + 16);

		nor_flash_opt();

		free(nortst.buff);
	}
}

/*******************************************************************************
 * @brief  nor_flash_opt
 * \param[in] none
 * \retval: none
 *******************************************************************************/
void nor_flash_opt(void)
{
	// spi mode check
	if (1 == nortst.check_req)
	{
		uint32_t amount = nortst.amount;
		uint32_t ticks;

		// 写入数据
		uint32_t dats = nortst.dat = nortst.dat_first;

		flash_dbg_print("nor flash check : start : R/W : %dMB\n", amount / (1024 * 1024));
		ticks = app_tick_get();
		for (nortst.addr = nortst.addr_start; nortst.addr < amount + nortst.addr_start; nortst.addr += SPI_NOR_PAGE_SIZE)
		{
			if (nortst.addr % SPI_NOR_SECTOR_SIZE == 0)
			{
				nor_sector_erase(nortst.addr);
			}

			for (int index = 0; index < SPI_NOR_PAGE_SIZE / 4; index++)
			{
				nortst.buff[index].uVal = nortst.dat++;
			}

			// 写入一页数据
			nor_write(nortst.addr, SPI_NOR_PAGE_SIZE, nortst.buff[0].v);

			if (0 == (nortst.addr & 0x03FFFF))
			{
				flash_dbg_print("nor flash check write data: %d %% \n", (nortst.addr - nortst.addr_start) * 100 / amount);
			}

			if (!nortst.check_req)
				break;
		}
		flash_dbg_print("nor flash write end: time : %d ms \n", app_tick_get() - ticks);

		// 读回校验
		ticks = app_tick_get();
		nortst.dat = dats;
		for (nortst.addr = nortst.addr_start; nortst.addr < amount + nortst.addr_start; nortst.addr += SPI_NOR_PAGE_SIZE)
		{
			if (nortst.addr % SPI_NOR_SECTOR_SIZE == 0)
			{
				// 读取一扇区数据
				nor_read(nortst.addr, SPI_NOR_SECTOR_SIZE, nortst.buff[0].v);

				for (int index = 0; index < SPI_NOR_SECTOR_SIZE / 4; index++)
				{
					if (nortst.buff[index].uVal != nortst.dat)
					{
						nortst.err++;
					}
					nortst.dat++;
				}

				if (nortst.err)
				{
					flash_dbg_print("nor flash check err: %d @ %x \n", nortst.err, nortst.addr);
					nortst.err = 0;
				}
			}

			if (!nortst.check_req)
				break;
		}

		nortst.check_req = 0;
		flash_dbg_print("nor flash read and check end : time : %d ms\n", app_tick_get() - ticks);

		set_u32(nortst.buff, 0, 4096 / 4);

		nortst.chk_cnt++;
		if (nortst.err)
			nortst.chk_err_cnt++;
	}

	// 读数据
	if (nortst.read_req)
	{
		uint32_t addr, tm_start, addr_end;

		addr_end = nortst.amount + nortst.addr_start;
		tm_start = app_tick_get();

		for (addr = nortst.addr_start; addr < addr_end; addr += 2048)
		{
			if (nortst.read_req == 1)
				nor_read(addr, 2048, nortst.buff[0].v);
			else
				nor_read(addr, 2048, nortst.buff[0].v);
		}

		nortst.tm_used = app_tick_get() - tm_start;

		flash_dbg_print("%s read: size = %d KB, time = %d, speed = %d KB/S\n",
						(nortst.read_req == 1) ? "spi" : "qpi",
						nortst.amount >> 10,
						nortst.tm_used,
						(nortst.amount >> 10) * 1000 / nortst.tm_used);
		// dbg_notice("nortst flash\nread end\n");
		nortst.read_req = 0;
	}

	// 单读测试
	if (nortst.sig_read_req)
	{
		memset(nortst.buff, 0, 4096);

		if (nortst.amount > 4096)
			nortst.amount = 4096;

		if (nortst.sig_read_req == 1)
			nor_read(nortst.addr_start, nortst.amount, nortst.buff[0].v);
		else
			nor_read(nortst.addr_start, nortst.amount, nortst.buff[0].v);

		dbg_u8_print("READ FLASH:\n", nortst.buff[0].v, nortst.amount, 32);

		nortst.sig_read_req = 0;
	}

	// 单写测试
	if (nortst.sig_write_req)
	{
		int cnt;

		nor_block_erase(nortst.addr_start);

		if (nortst.amount > 4096)
			nortst.amount = 4096;

		for (cnt = 0; cnt < 1024; cnt++)
		{
			nortst.buff[cnt].word.LW.uval = cnt * 2 + nortst.dat_first;
			nortst.buff[cnt].word.HW.uval = cnt * 2 + nortst.dat_first + 1;
		}

		if (nortst.sig_write_req == 1)
			nor_write_multi(nortst.addr_start, nortst.amount, &nortst.buff[0].v[2]);
		else
			nor_write_multi(nortst.addr_start, nortst.amount, &nortst.buff[0].v[2]);

		nortst.sig_write_req = 0;
	}
}

/*******************************************************************************
 * @brief: nor
 * @param argc 参数数量
 * @param argv 参数指针
 * @return 错误代码
 ******************************************************************************/
int nor(int argc, char *argv[])
{
	char *str;

	if (argc >= 2)
	{
		str = argv[1];
		// 命令格式 check [amount]
		// 第一个参数开启检测
		if (0 == comp_str(argv[1], "check", 10))
		{
			uu16 num;

			// 第二个参数设置检测数量，单位MB，数量为0关闭检测
			num = 1;
			if (argc >= 3)
			{
				num = string_to_i32(argv[2], 0);
				rang_limit(num, 0, 16);
			}

			if (0 != num)
				nortst.check_req = 1;
			else
				nortst.check_req = 0;

			nortst.amount = nMB(num);

			if (nortst.check_req)
			{
				dbg_puts("nor flash r/w check open !\n");
			}
			else
			{
				dbg_puts("nor flash r/w check close !\n");
			}

			dbg_print("nor flash check %dMB R/W: err/cnt :: %d/%d \n", nortst.amount / _MB_, nortst.chk_err_cnt, nortst.chk_cnt);
		}

		else if (0 == comp_str(argv[1], "read", 10))
		{
			// 命令格式 read [addr] [amount]
			nortst.addr_start = 0;
			nortst.amount = nKB(4);
			if (argc >= 3)
			{
				nortst.addr_start = string_to_i32(argv[2], 0);
				rang_limit(nortst.addr_start, 0, nMB(16));
			}
			if (argc >= 4)
			{
				nortst.amount = string_to_i32(argv[3], 0);
				rang_limit(nortst.amount, 0, nKB(4));
			}
			nortst.sig_read_req = 1;
		}
	}

	return 0;
}

SHELL_FUNX_EXPORT(nor, "[check MB /read addr KB] ");
